﻿/******************************************************************************
	Cobalt - utility libraries for building XNA games
	Copyright (C) 2008 Philip Djonov

	This program is free software; you can redistribute it and/or modify it
	under the terms of the GNU General Public License as published by the Free
	Software Foundation; either version 2 of the License, or (at your option)
	any later version.

	This program is distributed in the hope that it will be useful, but
	WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
	or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
	for more details.

	You should have received a copy of the GNU General Public License along
	with this program; if not, write to the
	
		Free Software Foundation, Inc.
		51 Franklin Street, Fifth Floor
		Boston, MA  02110-1301, USA.
******************************************************************************/

using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Cobalt.Framework.UI
{
	using Graphics;

	public abstract class Drawable : Element
	{
		public Property<Vector2> Size;
		public Property<Color> Color;

		protected Drawable( UiContext context )
			: base( context )
		{
			Color.SetSimpleValue( Microsoft.Xna.Framework.Graphics.Color.White );
		}
	}

	public class Image : Drawable
	{

		public Property<SpriteEffects> SpriteEffects;

		private Texture2D tex;
		private Rectangle srcRect;

		public Image( UiContext context, Texture2D texture )
			: base( context )
		{
			this.tex = texture;
			this.srcRect = new Rectangle( 0, 0, tex.Width, tex.Height );

			Size.SetSimpleValue( new Vector2( srcRect.Width, srcRect.Height ) );
		}

		public Image( UiContext context, Texture2D texture, Rectangle sourceRect )
			: base( context )
		{
			this.tex = texture;
			this.srcRect = sourceRect;

			Size.SetSimpleValue( new Vector2( srcRect.Width, srcRect.Height ) );
		}

		public Image( UiContext context, SpriteSheet sheet, int spriteIndex )
			: base( context )
		{
			sheet.GetSpriteInfo( spriteIndex, out tex, out srcRect );

			Size.SetSimpleValue( new Vector2( srcRect.Width, srcRect.Height ) );
		}

		public Image( UiContext context, SpriteSheet sheet, string spriteName )
			: base( context )
		{
			sheet.GetSpriteInfo( spriteName, out tex, out srcRect );

			Size.SetSimpleValue( new Vector2( srcRect.Width, srcRect.Height ) );
		}

		protected internal override void Render( IRenderContext context )
		{
			var color = Color.Evaluate();

			if( color.A == 0 )
				return;

			Vector2 size = Size.Evaluate();

			Vector2 scale = new Vector2();
			scale.X = srcRect.Width / size.X;
			scale.Y = srcRect.Height / size.Y;

			context.Draw( tex, new Vector2(), srcRect, color,
				0, new Vector2(), scale, SpriteEffects.Evaluate() );
		}
	}

	public class ColorFill : Drawable
	{
		public ColorFill( UiContext context )
			: base( context )
		{
		}

		protected internal override void Render( IRenderContext context )
		{
			var color = Color.Evaluate();

			if( color.A == 0 )
				return;

			var size = Size.Evaluate();
			
			var whiteImage = UiContext.WhiteImage;
			var scale = new Vector2( size.X / whiteImage.Width, size.Y / whiteImage.Height );

			context.Draw( whiteImage, Vector2.Zero, null, color,
				0, Vector2.Zero, scale, SpriteEffects.None );
		}
	}

	public class Label : Drawable
	{
		public Property<Font> Font;
		public Property<float> TextSize;

		public Property<TextPlacement> HorizontalPlacement;
		public Property<TextPlacement> VerticalPlacement;
		public Property<TextClippingMode> ClippingMode;

		public Label( UiContext context )
			: base( context )
		{
			TextSize.SetSimpleValue( 12 );

			HorizontalPlacement.SetSimpleValue( TextPlacement.Near );
			VerticalPlacement.SetSimpleValue( TextPlacement.Near );
			ClippingMode.SetSimpleValue( TextClippingMode.None );
		}

		/// <summary>
		/// The text to render.
		/// </summary>
		/// <remarks>
		/// This value is interpreted as follows:
		/// <list type="table">
		/// <listheader>
		///		<term>Type</term>
		///		<description>Interpreted As</description>
		/// </listheader>
		/// <item>
		///		<term><see cref="String"/></term>
		///		<description>The string is rendered directly.</description>
		/// </item>
		/// <item>
		///		<term><see cref="Char[]"/></term>
		///		<description>
		///		The array is treated as a null-terminated string.
		///		</description>
		/// </item>
		/// <item>
		///		<term><see cref="ArraySegment<Char>"/></term>
		///		<description>
		///		The given subrange of the character array is rendered.
		///		Note that assigning such a value will create garbage
		///		since <c>ArraySegment&lt;T&gt;</c> is a value type.
		///		</description>
		/// </item>
		/// <item>
		///		<term><see cref="TextBuffer"/></term>
		///		<description>
		///		The contents of the <c>TextBuffer</c> are drawn.
		///		</description>
		/// </item>
		/// <item>
		///		<term><c>null</c></term>
		///		<description>Nothing will be rendered.</description>
		/// </item>
		/// <item>
		///		<term>Any other</term>
		///		<description>
		///		<see cref="object.ToString()"/> will be called, and the
		///		result will be rendered to the screen. This will probably
		///		generate garbage. You have been warned.
		///		</description>
		/// </item>
		/// </list>
		/// </remarks>
		public Property<object> Text;

		protected internal override void Render( IRenderContext context )
		{
			var textVal = Text.Evaluate();
						
			if( textVal == null )
				return;

			var size = Size.Evaluate();

			var lle = UiContext.TextLayout;

			lle.Font = Font.Evaluate() ?? UiContext.DefaultFont;
			lle.TextSize = TextSize.Evaluate();

			lle.HorizontalPlacement = HorizontalPlacement.Evaluate();
			lle.VerticalPlacement = VerticalPlacement.Evaluate();
			lle.ClippingMode = ClippingMode.Evaluate();
			lle.DefaultColor = Color.Evaluate();

			lle.Reset();

			if( textVal is string )
			{
				lle.LayoutLines( (string)textVal, new Point(), size );
			}
			else if( textVal is char[] )
			{
				var text = (char[])textVal;

				int count = Array.IndexOf( text, '\0' );
				if( count == -1 )
					count = text.Length;

				lle.LayoutLines( new TextSource( text, 0, count ), new Point(), size );
			}
			else if( textVal is ArraySegment<char> )
			{
				var text = (ArraySegment<char>)textVal;

				lle.LayoutLines( text, new Point(), size );
			}
			else if( textVal is TextBuffer )
			{
				var text = (TextBuffer)textVal;

				lle.LayoutLines( text.Buffer, new Point(), size );
			}
			else
			{
				lle.LayoutLines( textVal.ToString(), new Point(), size );
			}

			lle.BeginGlyphLayout();

			TextLayout.GlyphLayout glyph;
			while( lle.LayoutNextGlyph( out glyph ) )
			{
				switch( glyph.Type )
				{
				case TextLayout.GlyphType.Glyph:
					context.Draw( glyph.Sheet, glyph.Origin, glyph.SourceRect,
						glyph.Color, 0, Vector2.Zero, glyph.Scale, SpriteEffects.None );
					break;
				}
			}
		}
	}

}